package com.gitee.wsl.io.file

import kotlin.time.ExperimentalTime
import kotlin.time.Instant


/*
class FileCacheStorage(
    private val directory: Dir,
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
)  {

    private val mutexes = ConcurrentMutableMap<String, Mutex>()

    init {
        directory.mkdirs()
    }

     suspend fun store(url: String, data: CachedResponseData): Unit = withContext(dispatcher) {
        val urlHex = key(url)
        val caches = readCache(urlHex).filterNot { it.varyKeys == data.varyKeys } + data
        writeCache(urlHex, caches)
    }

     suspend fun findAll(url: String): Set<CachedResponseData> {
        return readCache(key(url)).toSet()
    }

     suspend fun find(url: String, varyKeys: Map<String, String>): CachedResponseData? {
        val data = readCache(key(url))
        return data.find {
            varyKeys.all { (key, value) -> it.varyKeys[key] == value }
        }
    }

    private fun key(url: String) = url.toByteString().hex()// hex(MessageDigest.getInstance("SHA-256").digest(url.toString().encodeToByteArray()))

    private suspend fun writeCache(urlHex: String, caches: List<CachedResponseData>) = coroutineScope {
        val mutex = mutexes.computeIfAbsent(urlHex) { Mutex() }
        mutex.withLock {
            val channel = ByteChannel()
            try {
                File(directory, urlHex).writeBuffered {
                    launch {
                        channel.writeInt(caches.size)
                        for (cache in caches) {
                            writeCache(channel, cache)
                        }
                        channel.close()
                    }
                    channel.copyTo(this@writeBuffered)
                }
            } catch (cause: Exception) {
                LOGGER.trace("Exception during saving a cache to a file: ${cause.stackTraceToString()}")
            }
        }
    }

    private suspend fun readCache(urlHex: String): Set<CachedResponseData> {
        val mutex = mutexes.computeIfAbsent(urlHex) { Mutex() }
        mutex.withLock {
            val file = File(directory, urlHex)
            if (!file.exists()) return emptySet()

            try {
                file.readBuffered {
                        val channel = toByteReadChannel()
                        val requestsCount = channel.readInt()
                        val caches = mutableSetOf<CachedResponseData>()
                        for (i in 0 until requestsCount) {
                            caches.add(readCache(channel))
                        }
                        channel.discard()
                        return caches

                }
            } catch (cause: Exception) {
                Timber.e("Exception during cache lookup in a file: ${cause.stackTraceToString()}")
                return emptySet()
            }
        }
    }

    private suspend fun writeCache(channel: ByteChannel, cache: CachedResponseData) {
        channel.writeStringUtf8(cache.url.toString() + "\n")
        channel.writeInt(cache.statusCode.value)
        channel.writeStringUtf8(cache.statusCode.description + "\n")
        channel.writeStringUtf8(cache.version.toString() + "\n")
        val headers = cache.headers.flattenEntries()
        channel.writeInt(headers.size)
        for ((key, value) in headers) {
            channel.writeStringUtf8(key + "\n")
            channel.writeStringUtf8(value + "\n")
        }
        channel.writeLong(cache.requestTime.timestamp)
        channel.writeLong(cache.responseTime.timestamp)
        channel.writeLong(cache.expires.timestamp)
        channel.writeInt(cache.varyKeys.size)
        for ((key, value) in cache.varyKeys) {
            channel.writeStringUtf8(key + "\n")
            channel.writeStringUtf8(value + "\n")
        }
        channel.writeInt(cache.body.size)
        channel.writeFully(cache.body)
    }

    private suspend fun readCache(channel: ByteReadChannel): CachedResponseData {
        val url = channel.readUTF8Line()!!
        val status = HttpStatusCode(channel.readInt(), channel.readUTF8Line()!!)
        val version = HttpProtocolVersion.parse(channel.readUTF8Line()!!)
        val headersCount = channel.readInt()
        val headers = HeadersBuilder()
        for (j in 0 until headersCount) {
            val key = channel.readUTF8Line()!!
            val value = channel.readUTF8Line()!!
            headers.append(key, value)
        }
        val requestTime = GMTDate(channel.readLong())
        val responseTime = GMTDate(channel.readLong())
        val expirationTime = GMTDate(channel.readLong())
        val varyKeysCount = channel.readInt()
        val varyKeys = buildMap {
            for (j in 0 until varyKeysCount) {
                val key = channel.readUTF8Line()!!
                val value = channel.readUTF8Line()!!
                put(key, value)
            }
        }
        val bodyCount = channel.readInt()
        val body = ByteArray(bodyCount)
        channel.readFully(body)
        return CachedResponseData(
            url = url,
            expires = expirationTime,
            body = body
        )
    }
}*/

@OptIn(ExperimentalTime::class)
class CachedResponseData constructor(
    public val url: String,
    public val expires: Instant,
    public val body: ByteArray
)